#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>
#include <cstring>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

using namespace std;
#define Debug

const int num = 5;
static int number = 1; //用于进程编号

class channel
{
public:
    channel(int fd, pid_t id):ctrlfd(fd), workerid(id)
    {
        name = "channel-" + to_string(number++);
    }
public:
    int ctrlfd; //文件描述符
    pid_t workerid; //子进程id
    string name; //管道名称   
};

void Work()
{
    while(true)
    {
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code)); //规定每次读4个字节 
        if(n == sizeof(code))
        {
            if(!init.CheckSafe(code)) continue; //判断任务码是否合法
            init.RunTask(code); //执行任务码为code的任务
        }
        else if(n == 0)
        {
            break;
        }
        else
        {
            //do nothing
        }
    }
    cout << "child quit" << endl;
}

void PrintFd(const vector<int>& fds)
{
    cout << getpid() << "close fds: ";
    for(auto fd : fds)
    {
        cout << fd << " ";
    }
    cout << endl;
}

//创建信道
void CreateChannels(vector<channel>* c)
{
    vector<int> old;
    for(int i = 0; i < num; i++)
    {
        //1.创建管道
        int pipefd[2] = {0};
        int n= pipe(pipefd);
        assert(n == 0);

        //2.创建进程
        pid_t id = fork();
        assert(id != -1);

        //3.构建单向通信信道
        if(0 == id)
        {
            //child
            if(!old.empty())
            {
                for(auto fd: old)
                {
                    close(fd);
                }
                PrintFd(old);
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0); //重定向，子进程从标准输入中读即可
            Work();
            exit(0);
        }

        //father
        close(pipefd[0]);
        c->push_back(channel(pipefd[1], id));
        old.push_back(pipefd[1]);
    }
}

void PrintDebug(const vector<channel>& c)
{
    for(const auto &channel : c)
    {
        cout << channel.name << ", " << channel.ctrlfd << ", " << channel.workerid << endl;
    }
}

void SendCommand(const vector<channel>& c, bool flag, int num = -1)
{
    int pos = 0;
    while(true)
    {
        //2.1选择任务
        int command = init.SelectTask();
        //2.2选择信道(进程)
        const auto &channel = c[pos++];
        pos %= c.size();

        //debug
        cout << "send command " << init.ToDesc(command) << "[" << command << "]" << 
        " in " << channel.name << " worker is : " << channel.workerid << endl;

        //2.3发送任务
        write(channel.ctrlfd, &command, sizeof(command));
        //2.4判断是否要退出
        if(!flag)
        {
            num--;
            if(num <= 0) break;  
        }
        sleep(1);
    }
    cout << "SendCommand done..." << endl; 
}

void ReleaseChannels(vector<channel> c)
{
    //写法一:
    // for(const auto& channel : c)
    // {
    //     close(channel.ctrlfd); //关闭写端
    // }

    // for(const auto& channel : c)
    // {
    //     pid_t rid = waitpid(channel.workerid, nullptr, 0);
    //     if(rid == channel.workerid)
    //     {
    //         cout << "wait child: " << channel.workerid << " wait success" << endl;
    //     }
    // }

    //写法二:如果在CreateChannels中没有关闭多余的写端，最后会阻塞住
    for(const auto& channel : c)
    {
        close(channel.ctrlfd); //关闭写端
        waitpid(channel.workerid, nullptr, 0);
    }
}

int main()
{
    vector<channel> channels;
    //1.创建信道和进程
    CreateChannels(&channels);
    //PrintDebug(channels); 

    //2.开始完成任务
    const bool g_always_loop = true;
    SendCommand(channels, !g_always_loop, 10);

    //3.回收资源，让子进程退出，并且释放管道(父进程只需要关闭写端, 子进程读到0, 退出即可)
    ReleaseChannels(channels);
    // sleep(10);
    return 0;
}
